///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __ROOT__
#define __ROOT__

// Precompiler options
#include "OgrePrerequisites.h"

#include "OgreSingleton.h"
#include "OgreString.h"
#include "OgreSceneManagerEnumerator.h"
#include "OgreResourceGroupManager.h"
#include "OgreLodStrategyManager.h"
#include "OgreWorkQueue.h"

#include <exception>

namespace Ogre
{
typedef vector<RenderSystem*>::type RenderSystemList;

class _OgreExport Root : public Singleton<Root>, public MemAlloc_General
{
    // To allow update of active renderer if
    // RenderSystem::initialise is used directly
    friend class RenderSystem;
protected:
    RenderSystemList mRenderers;
    RenderSystem* mActiveRenderer;
    String mVersion;
	String mConfigFileName;
    bool mQueuedEnd;
    // In case multiple render windows are created, only once are the resources loaded.
    bool mFirstTimePostWindowInit;

    // Singletons
	Core::LogManager* mLogManager;
    ControllerManager* mControllerManager;
    SceneManagerEnumerator* mSceneManagerEnum;
	typedef deque<SceneManager*>::type SceneManagerStack;
	SceneManagerStack mSceneManagerStack;
    DynLibManager* mDynLibManager;
    ArchiveManager* mArchiveManager;
    MaterialManager* mMaterialManager;
    MeshManager* mMeshManager;
    ParticleSystemManager* mParticleManager;
    SkeletonManager* mSkeletonManager;
//     OverlayElementFactory* mPanelFactory;
//     OverlayElementFactory* mBorderPanelFactory;
//     OverlayElementFactory* mTextAreaFactory;
//     OverlayManager* mOverlayManager;
//    FontManager* mFontManager;
    ArchiveFactory *mZipArchiveFactory;
    ArchiveFactory *mEmbeddedZipArchiveFactory;
    ArchiveFactory *mFileSystemArchiveFactory;
	ResourceGroupManager* mResourceGroupManager;
	ResourceBackgroundQueue* mResourceBackgroundQueue;
	ShadowTextureManager* mShadowTextureManager;
	RenderSystemCapabilitiesManager* mRenderSystemCapabilitiesManager;
	ScriptCompilerManager *mCompilerManager;
    LodStrategyManager *mLodStrategyManager;

    Timing::PhiloTimer* mTimer;
    RenderWindow* mAutoWindow;
    Profiler* mProfiler;
    HighLevelGpuProgramManager* mHighLevelGpuProgramManager;
	ExternalTextureSourceManager* mExternalTextureSourceManager;
    CompositorManager* mCompositorManager;      
    unsigned long mNextFrame;
	Real mFrameSmoothingTime;
	bool mRemoveQueueStructuresOnClear;
	Real mDefaultMinPixelSize;

public:
	typedef vector<DynLib*>::type PluginLibList;
	typedef vector<Plugin*>::type PluginInstanceList;
protected:
	/// List of plugin DLLs loaded
    PluginLibList mPluginLibs;
	/// List of Plugin instances registered
	PluginInstanceList mPlugins;

	typedef map<String, MovableObjectFactory*>::type MovableObjectFactoryMap;
	MovableObjectFactoryMap mMovableObjectFactoryMap;
	uint32 mNextMovableObjectTypeFlag;
	// stock movable factories
	MovableObjectFactory* mEntityFactory;
	MovableObjectFactory* mLightFactory;
	//MovableObjectFactory* mBillboardSetFactory;
	//MovableObjectFactory* mManualObjectFactory;

	typedef map<String, RenderQueueInvocationSequence*>::type RenderQueueInvocationSequenceMap;
	RenderQueueInvocationSequenceMap mRQSequenceMap;

	/// Are we initialised yet?
	bool mIsInitialised;

	WorkQueue* mWorkQueue;

	///Tells whether blend indices information needs to be passed to the GPU
	bool mIsBlendIndicesGpuRedundant;
	///Tells whether blend weights information needs to be passed to the GPU
	bool mIsBlendWeightsGpuRedundant;

    
    void loadPlugins( const String& pluginsfile = "plugins.cfg" );
	
	void initialisePlugins();
	
	void shutdownPlugins();

	
    void unloadPlugins();

    // Internal method for one-time tasks after first window creation
    void oneTimePostWindowInit(void);

    
    set<FrameListener*>::type mFrameListeners;

    
    set<FrameListener*>::type mRemovedFrameListeners;

    
    enum FrameEventTimeType {
        FETT_ANY = 0, 
		FETT_STARTED = 1, 
		FETT_QUEUED = 2, 
		FETT_ENDED = 3, 
		FETT_COUNT = 4
    };

    /// Contains the times of recently fired events
	typedef deque<unsigned long>::type EventTimesQueue;
    EventTimesQueue mEventTimes[FETT_COUNT];

    Real calculateEventTime(unsigned long now, FrameEventTimeType type);

	void populateFrameEvent(FrameEventTimeType type, FrameEvent& evtToUpdate);

public:

    Root(const String& pluginFileName = "plugins.cfg", 
		const String& configFileName = "ogre.cfg", 
		const String& logFileName = "Ogre.log");
    ~Root();

    void saveConfig(void);

    bool restoreConfig(void);

    bool showConfigDialog(void);

    void addRenderSystem(RenderSystem* newRend);

    const RenderSystemList& getAvailableRenderers(void);

    RenderSystem* getRenderSystemByName(const String& name);

    void setRenderSystem(RenderSystem* system);

    RenderSystem* getRenderSystem(void);

    RenderWindow* initialise(bool autoCreateWindow, const String& windowTitle = "OGRE Render Window",
                                const String& customCapabilitiesConfig = StringUtil::BLANK);

	bool isInitialised(void) const { return mIsInitialised; }

    void useCustomRenderSystemCapabilities(RenderSystemCapabilities* capabilities);

	bool getRemoveRenderQueueStructuresOnClear() const { return mRemoveQueueStructuresOnClear; }

	void setRemoveRenderQueueStructuresOnClear(bool r) { mRemoveQueueStructuresOnClear = r; }

	void addSceneManagerFactory(SceneManagerFactory* fact);

	void removeSceneManagerFactory(SceneManagerFactory* fact);

	const SceneManagerMetaData* getSceneManagerMetaData(const String& typeName) const;

	SceneManagerEnumerator::MetaDataIterator getSceneManagerMetaDataIterator(void) const;

	SceneManager* createSceneManager(const String& typeName, 
		const String& instanceName = StringUtil::BLANK);

	SceneManager* createSceneManager(SceneTypeMask typeMask, 
		const String& instanceName = StringUtil::BLANK);

	void destroySceneManager(SceneManager* sm);

	SceneManager* getSceneManager(const String& instanceName) const;

	bool hasSceneManager(const String& instanceName) const;
	
	SceneManagerEnumerator::SceneManagerIterator getSceneManagerIterator(void);

    TextureManager* getTextureManager(void);

    MeshManager* getMeshManager(void);

    String getErrorDescription(long errorNumber);

    void addFrameListener(FrameListener* newListener);

    void removeFrameListener(FrameListener* oldListener);

    void queueEndRendering(void);

    void startRendering(void);

    bool renderOneFrame(void);

	bool renderOneFrame(Real timeSinceLastFrame);

    void shutdown(void);

    void addResourceLocation(const String& name, const String& locType, 
		const String& groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
		bool recursive = false);

	void removeResourceLocation(const String& name, 
		const String& groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

	DataStreamPtr createFileStream(const String& filename, const String& groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
		bool overwrite = false, const String& locationPattern = StringUtil::BLANK);
	
	DataStreamPtr openFileStream(const String& filename, const String& groupName = ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
		const String& locationPattern = StringUtil::BLANK);

    void convertColourValue(const ColourValue& colour, uint32* pDest);

    RenderWindow* getAutoCreatedWindow(void);

	RenderWindow* createRenderWindow(const String &name, unsigned int width, unsigned int height, 
		bool fullScreen, const NameValuePairList *miscParams = 0) ;

	bool createRenderWindows(const RenderWindowDescriptionList& renderWindowDescriptions,
		RenderWindowList& createdWindows);

    RenderTarget* detachRenderTarget( RenderTarget* pWin );

    RenderTarget* detachRenderTarget( const String & name );

    void destroyRenderTarget(RenderTarget* target);

    void destroyRenderTarget(const String &name);

    RenderTarget * getRenderTarget(const String &name);

	void loadPlugin(const String& pluginName);

	void unloadPlugin(const String& pluginName);

	void installPlugin(Plugin* plugin);

	void uninstallPlugin(Plugin* plugin);

	const PluginInstanceList& getInstalledPlugins() const { return mPlugins; }

    Timing::PhiloTimer* getTimer(void);

    bool _fireFrameStarted(FrameEvent& evt);
    
    bool _fireFrameRenderingQueued(FrameEvent& evt);

    bool _fireFrameEnded(FrameEvent& evt);
    
    bool _fireFrameStarted();
    
    bool _fireFrameRenderingQueued();
    
    bool _fireFrameEnded();

    unsigned long getNextFrameNumber(void) const { return mNextFrame; }

    SceneManager* _getCurrentSceneManager(void) const;
    
    void _pushCurrentSceneManager(SceneManager* sm);
	
	void _popCurrentSceneManager(SceneManager* sm);

    bool _updateAllRenderTargets(void);

    bool _updateAllRenderTargets(FrameEvent& evt);

	RenderQueueInvocationSequence* createRenderQueueInvocationSequence(const String& name);

	RenderQueueInvocationSequence* getRenderQueueInvocationSequence(const String& name);

	void destroyRenderQueueInvocationSequence(const String& name);

	void destroyAllRenderQueueInvocationSequences(void);

    static Root& getSingleton(void);
    
    static Root* getSingletonPtr(void);

	void clearEventTimes(void);

	void setFrameSmoothingPeriod(Real period) { mFrameSmoothingTime = period; }
	
	Real getFrameSmoothingPeriod(void) const { return mFrameSmoothingTime; }

	void addMovableObjectFactory(MovableObjectFactory* fact, bool overrideExisting = false);
	
	void removeMovableObjectFactory(MovableObjectFactory* fact);
	/// Checks whether a factory is registered for a given MovableObject type
	bool hasMovableObjectFactory(const String& typeName) const;
	/// Get a MovableObjectFactory for the given type
	MovableObjectFactory* getMovableObjectFactory(const String& typeName);
	
	uint32 _allocateNextMovableObjectTypeFlag(void);

	typedef ConstMapIterator<MovableObjectFactoryMap> MovableObjectFactoryIterator;
	
	MovableObjectFactoryIterator getMovableObjectFactoryIterator(void) const;

	unsigned int getDisplayMonitorCount() const;

	WorkQueue* getWorkQueue() const { return mWorkQueue; }

	void setWorkQueue(WorkQueue* queue);
		
	void setBlendIndicesGpuRedundant(bool redundant) {	mIsBlendIndicesGpuRedundant = redundant; }
	
	bool isBlendIndicesGpuRedundant() const { return mIsBlendIndicesGpuRedundant; }

	void setBlendWeightsGpuRedundant(bool redundant) { mIsBlendWeightsGpuRedundant = redundant; }
	
	bool isBlendWeightsGpuRedundant() const { return mIsBlendWeightsGpuRedundant; }
	
	void setDefaultMinPixelSize(Real pixelSize) { mDefaultMinPixelSize = pixelSize; }

	Real getDefaultMinPixelSize() { return mDefaultMinPixelSize; }

};

} // Namespace Ogre
#endif
